//
//  BtcSeriesCoinApi.cpp
//  Test
//
//  Created by xgc on 12/19/17.
//  Copyright © 2017 xgc. All rights reserved.
//

#include "QtumSeriesCoinApi.hpp"
#include "../utils/KeyCrypterScrypt.h"
#include "../common/BtSeed.hpp"
#include "../utils/Common.h"
#include "QtumCreateSignTransactionParams.hpp"


bool QtumSeriesCoinApi::getTokenTransferBinary(string address, dev::u256 value, vector<uint8_t>& encode)
{
    int len = address.length();

    if(len != 42 && len != 40) {
        return false;
    }

    int istart = 0;
    if(len == 42) {
        istart = 2;
    }
    for(int i = istart; i < len  - istart; i++) {
        if(!hex(address[i])){
            return false;
        }
    }

    string stripAddress = address.substr(istart, 40);

    uint32_t methodID = 0xbb9c05a9;
    uint8_t* p = (uint8_t*)&methodID;

    encode.assign(p, p + 4);

    dev::bytes addressBytes = dev::fromUserHex(stripAddress);
    int fillzero = 12;

    for(int i = 0; i < fillzero; i++) {
        encode.push_back(0);
    }

    encode.insert(encode.end(), addressBytes.begin(), addressBytes.end());

    string strValue = value.str(0, std::ios_base::hex);
    len = strValue.length();

    if(len <= 0 || len > 64) {
        return false;
    }

    fillzero = 64 - len;

    if(fillzero > 0) {
        string strZero;
        strZero.assign(fillzero, '0');
        strValue = strZero + strValue;
    }

    dev::bytes valueBytes = dev::fromUserHex(strValue);
    encode.insert(encode.end(), valueBytes.begin(), valueBytes.end());

    return true;
}

bool QtumSeriesCoinApi::createSignTransaction(CreateSignTransactionParams* params, vector<ChildNumber> path, NetParams netparams, map<string, string> & resultMap)
{
    QtumCreateSignTransactionParams* btcParams = (QtumCreateSignTransactionParams*)params;
    string priKey = btcParams->getPriKey();
    NetworkType netType = netparams.getNetType();
    CoinType coinType = netparams.getCoinType();
    string coinName = netparams.getSymbol();


    uint16_t address_version = 0;
    uint16_t address = 0;
    uint8_t wif = 0;

    //address = getP2kh(coinType, netType, netparams);
    //wif = getKeyPrefixe(coinType, netType, netparams);
    address = netparams.getP2KH();
    wif = netparams.getKeyprefixes();

    address_version = ec_private::to_version(address, wif);


    ec_secret secret;
    ec_compressed compressed;

    if(priKey.length() == 0)
    {
        string tmpPriKey = getPriKeyFromBtSeed(btcParams->getBtSeed(), btcParams->getPassword(),
                                               path, netparams);

        ec_private ecPri(tmpPriKey, address_version);
        secret = ecPri.secret();
        ec_public ecPub = ecPri.to_public();
        compressed = ecPub.point();
    }
    else //private key sign
    {
        ec_private ecPri(priKey, address_version);
        secret = ecPri.secret();
        ec_public ecPub = ecPri.to_public();
        compressed = ecPub.point();

        payment_address address = ecPub.to_payment_address(address_version);
        string addr = address.encoded();

        printf("0");
    }

    data_chunk pubkey = to_chunk(compressed);

    vector<BtcvoutForm> btcvoutFormList = btcParams->getBtcvoutFormList();
    int size = btcvoutFormList.size();

    uint64_t sum = 0;
    transaction tx = transaction();
    tx.set_version(netparams.getVersion());

    int estimateSignLen = 0;

    for(int i = 0; i < size; i++)
    {
        hash_digest utxoHash;
        decode_hash(utxoHash, btcvoutFormList[i].getHash());
        output_point utxo(utxoHash, btcvoutFormList[i].getN());
        sum += btcvoutFormList[i].getValue();

        bool isCoinBase = btcvoutFormList[i].getCoinBase();
        if(isCoinBase) {
            estimateSignLen += 73;
        } else {
            estimateSignLen += 107;
        }
        input input1 = input();
        input1.set_previous_output(utxo);
        input1.set_sequence(0xffffffff);
        input1.value_ = btcvoutFormList[i].getValue();

        tx.inputs().push_back(input1);
    }


    string sendAddress = btcParams->getSendAddress();

    dev::bytes addressBytes = dev::fromUserHex(sendAddress);
    short_hash destHash;

    int i = 0;
    for (uint8_t & x : addressBytes) {
        destHash[i] = x;
        i++;
    }


    uint64_t sendValue = btcParams->getSendValue();
    uint64_t gaslimit = btcParams->getGasLimit();
    uint64_t gasprice = btcParams->getGasPrice();
    string tokenAddress = btcParams->getTokenAddress();
    short_hash tokenHash;
    payment_address tokenPayAddress(tokenAddress);//receive address
    tokenHash = tokenPayAddress.hash();

    string strTokenHash = dev::toHex(tokenHash.begin(), tokenHash.end(), "");

    dev::u256 tokenVaule = btcParams->getTokenValue();

    vector<uint8_t> transferdata;
    getTokenTransferBinary(strTokenHash, tokenVaule, transferdata);

    vector<uint8_t> bindata = tx.qtum_token_to_data(0x04, gaslimit, gasprice, transferdata, destHash, 0xc2);
    script outputScript1(bindata, false);
    output output1(sendValue, outputScript1);
    tx.outputs().push_back(output1);

    uint64_t fee = 0;
    size_t txlen = 0;

    short_hash fromHash;
    string from = btcParams->getFromAddress();

    payment_address fromAddress(from);
    fromHash = fromAddress.hash();

    script outputScript2 = script().to_pay_key_hash_pattern(fromHash);
    output output2(0, outputScript2);
    tx.outputs().push_back(output2);

    txlen = tx.serialized_size() + estimateSignLen;
    fee = txlen * btcParams->getFeePerKb() / 1000;

    uint64_t changeValue = sum  - sendValue - fee - gaslimit * gasprice;
    tx.outputs()[1].set_value(changeValue);


    uint8_t sign_type = all;

    for(int j = 0; j < size; j++ )
    {
        endorsement sig;
        script lockingScript;

        bool isCoinBase = btcvoutFormList[j].getCoinBase();
        if(isCoinBase) {
            lockingScript = script().to_pay_public_key_pattern(pubkey);
        } else {
            lockingScript = script().to_pay_key_hash_pattern(bitcoin_short_hash(pubkey));
        }

        if(lockingScript.create_endorsement(sig, secret, lockingScript, tx, j, sign_type))
        {
            std::cout << "Signature: " << std::endl;
            std::cout << encode_base16(sig) << "\n" << std::endl;
        }

        operation::list sigScript;
        sigScript.push_back(operation(sig));
        if(!isCoinBase) {
            sigScript.push_back(operation(pubkey));
        }
        script unlockingScript(sigScript);

        tx.inputs()[j].set_script(unlockingScript);
    }

    string hex = encode_base16(tx.to_data());

    size_t newLen = tx.serialized_size();

    std::cout << "Raw Transaction len: " << newLen << std::endl;
    std::cout << "Raw Transaction: " << std::endl;
    std::cout << hex << std::endl;

    std::stringstream strIO;
    string strLen;
    strIO << newLen;
    strIO >> strLen;
    strIO.clear();

    string strFee;
    strIO << fee;
    strIO >> strFee;

    resultMap.insert(make_pair("hex", hex));
    resultMap.insert(make_pair("length", strLen));
    resultMap.insert(make_pair("fee", strFee));


    return true;
}


